#pragma once 
#include<iostream>
#include<poll.h>
#include"Socket.hpp"

//select的缺陷
//1.等待的fd是有上限的
//2.输入输出参数是由"同一个"变量控制的，每次输出的结果都会改变rfds的值，必须重新对rfds重置，遍历麻烦
static const uint16_t default_port=8080;
static const int max_num_array=1024;
static const int default_data=-1;//fd_array数组的默认初始值 
static const short non_event=0;   
static const int timeout=3000;//每隔3秒返回一次结果

//引出——> poll(struct pollfd* fds, nfds_t nfds, int timeout); 
//struct pollfd结构体内有输入参数event，和输出参数revent,这是两个short类型的位图，每个比特位的内容，就是用户与内核交流的信息
class PollServer
{
public:
    PollServer(uint16_t port=default_port):_port(port)
    {
        for(int i=1;i<max_num_array;i++)
        {
            //初始化数组值
            _event_fd[i].fd=default_data;
            _event_fd[i].events=non_event;
            _event_fd[i].revents=non_event;
        }
    }
    bool Init()
    {
        _listensock.Socket();//创建套接字
        _listensock.Bind(_port);//绑定套接字
        _listensock.Listen();//将套接字设为监听状态
    }
    void Hander_Link()
    {
        for(int i=0;i<max_num_array;i++)
        {
            if(_event_fd[i].revents & POLLIN)//遍历一遍_event_fd数组，判断哪些fd是"读"响应的结果
            {
                //判断fd_array[i]是“监听”套接字，还是read阻塞响应套接字
                if(_event_fd[i].fd==_listensock.sockfd())//是“监听”套接字，就先accpte从内核拿到上层，再仍回select
                {
                    string client_ip;
                    uint16_t client_port;
                    int sock=_listensock.Accept(client_ip,client_port);//将新建立的链接拿到上层
                    cout<<"accept socket successfully,sock:"<<sock<<",errno:"<<errno<<",strerror:"<<strerror(errno)<<endl;

                    //ssize_t n=read(sock,buffer,sizeof(buffer));//不能直接read，因为若对方没向服务端发消息的话，会阻塞等待
                    int pos=0;
                    for(int pos=0;pos<max_num_array;pos++)
                        if(_event_fd[pos].fd==default_data) {
                            _event_fd[pos].fd=sock;//所以，我们需要把sock再次交给poll
                            _event_fd[pos].events=POLLIN;//让poll帮我们判断sock的"读"事件是否成立
                            break;
                        }
                    if(pos==max_num_array)//_event_fd数组满了，需要扩容
                    {
                        cerr<<"server is full,need expansion..."<<endl;
                        //扩容机制
                    }
                }
                else//是read响应套接字，就调read直接从文件缓冲区把数据读出来
                {
                    char buffer[1024];
                    ssize_t n=read(_event_fd[i].fd,buffer,sizeof(buffer)-1);//bug  tcp面向字节流，读取的报文可能不完整——>Encode、Decode + 序列化、反序列化
                    if(n>0)
                    {
                        buffer[n-1]='\0';//buffer中含回车
                        cout<<"receive a new data:"<<buffer<<endl;
                    }
                    else if(n==0)
                    {
                        cout<<"counterpart have closed sock, so i will close it..."<<endl;
                        close(_event_fd[i].fd);
                        _event_fd[i].fd=default_data;//链接都已经关了，select就没必要再继续关心这个fd了
                    }
                    else 
                    {
                        cerr<<"read failed，so i will close it..."<<endl;
                        close(_event_fd[i].fd);
                        _event_fd[i].fd=default_data;//读取出错，select也没必要再继续关心这个fd了
                    }
                }
            }
        }
    }
    void Start()
    {
        _event_fd[0].fd=_listensock.sockfd();
        _event_fd[0].events=POLLIN;//告诉内核：关注“监听”套接字的“读”事件
        while(true)
        {
            int n=poll(_event_fd,max_num_array,timeout);
            switch(n)
            {
                case 0:
                    cout<<"等待超时..."<<endl;
                    break;
                case -1:
                    cerr<<"等待结果出错..."<<"errno:"<<errno<<",strerror:"<<strerror(errno)<<endl;
                    break;
                default:
                    cout<<"建立了一个新的链接..."<<endl;
                    Hander_Link();//select已经承担了“等待新链接”的工作，再次accept时，可将fd直接拿到上层，无需等待
                    break;
            }
        }
    }

    ~PollServer()
    {
        _listensock.Close();
    }

private:
    Sock _listensock;
    uint16_t _port;
    struct pollfd _event_fd[max_num_array];//可以动态扩容
};
